﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Meta.Numerics.Statistics.Distributions;

namespace ExcelAddIn1
{
    /**
     * Implementation of a simple linear regression.
     */
    class Test_LinearRegression : Test
    {
        public Test_LinearRegression(DataContainer data)
        {
            this.data = data;
            res = new List<String>();
        }

        public override string GetInfo()
        {
            String s = "Performs a linear regression between two datasets and tests for significance. ";
            s += "The y's (dependent variable) must be approximatelly normally distributed for each x (indepentent variable). ";
            s += "The y’s must have a constant amount of spread (stdev) for each value of x.";
            return s;
        }

        public override void RunTest()
        {
            res.Add("Linear Regression");

            //Error check
            if (data.GetNoSets() != 2)
            {
                res.Add("Two datasets is required!");
                return;
            }

            //Get data sets
            DataSet d1 = data.GetDataSet(0);
            DataSet d2 = data.GetDataSet(1);
            double n = (double)d1.GetN();

            //Error check 2
            if (d1.GetN() != d2.GetN())
            {
                res.Add("Both datasets must have equal sample sizes!");
                return;
            }

            double sumX = 0.0;
            double sumY = 0.0;
            double sumXY = 0.0;
            double sumX2 = 0.0;
            double sumY2 = 0.0;

            for (int i = 0; i < n; i++)
            {
                double x = d1.GetValue(i);
                double y = d2.GetValue(i);

                sumX += x;
                sumY += y;
                sumXY += x * y;
                sumX2 += Math.Pow(x, 2);
                sumY2 += Math.Pow(y, 2);
            }

            //Calculate line of best fit
            double b = (n * sumXY - sumX * sumY) / (n * sumX2 - Math.Pow(sumX, 2));
            double a = sumY / n - b * sumX / n;

            //Calculate coefficient of determination
            double SST = sumY2 - Math.Pow(sumY, 2) / n;
            double SSE = sumY2 - a * sumY - b * sumXY;
            double SSR = SST - SSE;

            double R2 = SSR / SST;

            //Calculate F-score
            double F = SSR / (SSE / (n - 2.0));

            //Find critical F
            double DF1 = 1.0;
            double DF2 = n - 2.0;
            FisherDistribution fdist = new FisherDistribution(DF1, DF2);
            double FcHigh = fdist.InverseRightProbability(alpha);
            double FcLow = fdist.InverseLeftProbability(alpha);

            //Calculate p
            double p = fdist.RightProbability(F);

            //Results
            res.Add(";" + d1.GetName() + ";" + d2.GetName() + ";-");
            res.Add("N;" + d1.GetN() + ";" + d2.GetN() + ";-");
            res.Add("DoF1;" + (int)DF1);
            res.Add("DoF2;" + (int)DF2);
            res.Add("Mean;" + d1.GetMean().ToString("F2") + ";" + d2.GetMean().ToString("F2"));
            res.Add("StDev;" + d1.GetStDev().ToString("F2") + ";" + d2.GetStDev().ToString("F2"));
            
            if (b < 0)
            {
                res.Add("Line;ŷ=" + a.ToString("F2") + "" + b.ToString("F2") + "x");
            }
            else
            {
                res.Add("Line;ŷ=" + a.ToString("F2") + "+" + b.ToString("F2") + "x");
            }

            res.Add("α;" + alpha + ";;-");
            res.Add("P-value;" + p.ToString("F3"));
            res.Add("R^2;" + R2.ToString("F2"));
            res.Add("F-score;" + F.ToString("F2"));
            res.Add("F-crit;" + FcHigh.ToString("F2"));
            if (F < FcLow || F > FcHigh)
            {
                res.Add("Result;Significant relationship at level " + p.ToString("F3"));
            }
            else
            {
                res.Add("Result;No relationship (significance level " + p.ToString("F3"));
            }
            res.Add(";;;-");
        }
    }
}
